﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using COFE.Core.Filter;
using COFE.Data.Utils.ThirdParty;
using COFE.Core.Validater;

namespace COFE.Data.Filter
{
    public class PropertyEntryFilterExpression :
         EntryFilterExpression<COFE.Core.Filter.PropertyEntryFilter>
    {
        public PropertyEntryFilterExpression()
            : base()
        {

        }

        public override Expression<Func<Entry, bool>> getExpression(
            IEntryFilter filter, IOptionFilter[] options)
        {
            Expression<Func<Entry, bool>> exp;

            if (COFEDBConfiguration.Config.ApplySupported)
                exp = getMainFilterExpression(filter, options);
            else
            {
                var entryFilter = filter as COFE.Core.Filter.PropertyEntryFilter;
                int propertyId = (int)entryFilter.Property;
                exp =
                    (e) => e.EntryProperties.Any((ep) => ep.PropertyId == propertyId);
            }

            if (filter.Negative)
                return exp.Not();
            else return exp;
        }

        private Expression<Func<Entry, bool>> getMainFilterExpression(IEntryFilter filter, IOptionFilter[] options)
        {
            var entryFilter = filter as COFE.Core.Filter.PropertyEntryFilter;
            int propertyId = (int)entryFilter.Property;
            int op = entryFilter.Value.StartsWith(">") ? 1 :
                   entryFilter.Value.StartsWith("<") ? -1 : 0;
            Type propertyType = PropertyTypeExtension
                        .GetDefaultPropertyType(entryFilter.Property);
            string valueStr = entryFilter.Value.TrimStart('<', '>');


            switch (propertyType.ToString())
            {
                #region Int16, Int32, Int64
                case "System.Int16":
                case "System.Int32":
                case "System.Int64":
                    var intValue = Int64.Parse(valueStr);
                    switch (op)
                    {
                        case 0:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Int32).Value == intValue;
                        case 1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Int32).Value >= intValue;
                        case -1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Int32).Value <= intValue;
                    }
                    break;
                #endregion

                #region Single
                case "System.Single":
                    var sigValue = Single.Parse(valueStr);
                    switch (op)
                    {
                        case 0:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Single).Value == sigValue;
                        case 1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Single).Value >= sigValue;
                        case -1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_Single).Value <= sigValue;
                    }
                    break;
                #endregion
                #region DateTime
                case "System.DateTime":
                    var dtValue = DateTime.Parse(valueStr);
                    switch (op)
                    {
                        case 0:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_DateTime).Value == dtValue;
                        case 1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_DateTime).Value >= dtValue;
                        case -1:
                            return (e) =>
                                (e.EntryProperties.FirstOrDefault(
                                    (ep) => ep.PropertyId == propertyId) as
                                    EntryProperty_DateTime).Value <= dtValue;
                    }
                    break;
                #endregion
                #region Guid
                case "System.Guid":
                    Guid guidValue = new Guid(valueStr);
                    return (e) =>
                            (e.EntryProperties.FirstOrDefault(
                            (ep) => ep.PropertyId == propertyId) as
                            EntryProperty_Guid).Value == guidValue;
                #endregion
                #region Bool
                case "System.Boolean":
                    bool boolValue = valueStr.Equals("true");
                    return (e) =>
                            (e.EntryProperties.FirstOrDefault(
                            (ep) => ep.PropertyId == propertyId) as
                            EntryProperty_Bool).Value == boolValue;
                #endregion
                #region String
                case "System.String":
                    return (e) =>
                            (e.EntryProperties.FirstOrDefault(
                            (ep) => ep.PropertyId == propertyId) as
                            EntryProperty_String).Value.Contains(valueStr);
                #endregion

            }
            return e => true;
        }

        public override Expression<Func<Entry, bool>> getOutputExpression(
            IEntryFilter filter, IOptionFilter[] options)
        {
            if (!(COFEDBConfiguration.Config.ApplySupported))
                return getMainFilterExpression(filter, options);
            else return e => true;
        }

    }
}
